package game.poker.holdem;

import game.poker.holdem.card.Card;
import game.poker.holdem.card.Rank;
import game.poker.holdem.card.RankComparator;
import game.poker.holdem.card.RankComparators;
import game.poker.holdem.card.Suit;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public enum HoldEmUtils {
    ;
    
    private static final Map<Suit, Map<Rank, String>> unicodeStringLiteralMap;
    
    static {
        
        Suit[] suits = { 
                Suit.SPADES, 
                Suit.HEARTS, 
                Suit.DIAMONDS, 
                Suit.CLUBS
        };
        
        Rank[] ranks = {
                Rank.ACE,
                Rank.TWO,
                Rank.THREE,
                Rank.FOUR,
                Rank.FIVE,
                Rank.SIX,
                Rank.SEVEN,
                Rank.EIGHT,
                Rank.NINE,
                Rank.TEN,
                Rank.JACK,
                Rank.QUEEN,
                Rank.KING
        };
        
        char firstSurrogate = 0xD83C;
        char secondSurrogate = 0xDCA1;
        
        unicodeStringLiteralMap = new HashMap<Suit, Map<Rank, String>>();
        
        for(Suit suit : suits) {
            Map<Rank, String> rankCharacterMap = new HashMap<Rank, String>();
            unicodeStringLiteralMap.put(suit, rankCharacterMap);
            
            for(Rank rank : ranks) {
                String unicodeCharacter = String.valueOf(new char[]{firstSurrogate, secondSurrogate});
                rankCharacterMap.put(rank, unicodeCharacter);                
                secondSurrogate++;
                if(secondSurrogate == 0xDCAC ||
                   secondSurrogate == 0xDCBC ||
                   secondSurrogate == 0xDCCC ||
                   secondSurrogate == 0xDCDC ||
                   secondSurrogate == 0xDCAF || 
                   secondSurrogate == 0xDCBF || 
                   secondSurrogate == 0xDCCF) {
                    secondSurrogate++;
                }
                if(secondSurrogate == 0xDCB0 ||
                   secondSurrogate == 0xDCC0 ||
                   secondSurrogate == 0xDCD0) {
                    secondSurrogate++;
                }
            }
        }
    }

    
    /**
     * The list of cards must be (pre-) sorted using using the same rank comparator.
     * 
     * Returns -1 if there is no straight.
     * 
     * @param cards
     * @param rankComparator
     * @return
     */
    public static int endIndexOfHighestStraight(List<Rank> ranks, RankComparator rankComparator) {
        int endIndex = ranks.size() - 1;
        int startIndex;
        while((startIndex = endIndex - 4) >= 0) {
            boolean isStraight = true;
            for(int index = startIndex; index < endIndex; index++) {
                Rank low = ranks.get(index);
                Rank high = ranks.get(index + 1);
                if(!rankComparator.isConsecutive(low, high)) {
                    isStraight = false;
                    break;
                }
            }
            if(isStraight) {
                break;
            }
            endIndex--;
        }
        return (startIndex < 0) ? -1 : endIndex;
    }
    
    public static int endIndexOfHighestStraight(List<Rank> ranks) {
        return endIndexOfHighestStraight(ranks, RankComparators.ACE_HIGH);
    }
    
    public static String toStringUnicodeImage(Card card) {
        Suit suit = card.getSuit();
        Rank rank = card.getRank();
        return unicodeStringLiteralMap.get(suit).get(rank);
    }
    
    public static Map<Rank, List<Card>> partitionRankWise(List<Card> cards) {
        Map<Rank, List<Card>> rankCardsMap = new HashMap<Rank, List<Card>>();       
        for(Card card : cards) {
            Rank rank = card.getRank();
            List<Card> rankCards = rankCardsMap.get(rank);
            if(rankCards == null) {
                rankCards = new ArrayList<Card>();
                rankCardsMap.put(rank, rankCards);
            }
            rankCards.add(card);
        }
        return rankCardsMap;
    }
    
    public static Map<Suit, List<Card>> partitionSuitWise(List<Card> cards) {
        Map<Suit, List<Card>> suitCardsMap = new HashMap<Suit, List<Card>>();       
        for(Card card : cards) {
            Suit suit = card.getSuit();
            List<Card> suitCards = suitCardsMap.get(suit);
            if(suitCards == null) {
                suitCards = new ArrayList<Card>();
                suitCardsMap.put(suit, suitCards);
            }
            suitCards.add(card);            
        }
        return suitCardsMap;
    }
    
    public static List<Card> mostFrequentSuitCards(List<Card> cards) {
        Map<Suit, List<Card>> suitCardsMap = partitionSuitWise(cards);
        List<Card> mostFrequentSuitCards = Collections.emptyList();
        for(Suit suit : suitCardsMap.keySet()) {
            List<Card> suitCards = suitCardsMap.get(suit);
            if(suitCards.size() > mostFrequentSuitCards.size()) {
                mostFrequentSuitCards = suitCards;
            }
        }
        return mostFrequentSuitCards;
    }
    
    public static void main(String[] args) {
        List<Rank> data1 = new ArrayList<Rank>();
        System.out.println(endIndexOfHighestStraight(data1) + " - " + data1);
        List<Rank> data2 = Arrays.asList(Rank.TWO);
        System.out.println(endIndexOfHighestStraight(data2) + " - " + data2);
        List<Rank> data3 = Arrays.asList(Rank.TWO, Rank.THREE, Rank.FOUR, Rank.FIVE);
        System.out.println(endIndexOfHighestStraight(data3) + " - " + data3);
        List<Rank> data4 = Arrays.asList(Rank.ACE, Rank.TWO, Rank.THREE, Rank.FOUR, Rank.FIVE);
        System.out.println(endIndexOfHighestStraight(data4) + " - " + data4);
        List<Rank> data4a = Arrays.asList(Rank.ACE, Rank.TWO, Rank.THREE, Rank.FOUR, Rank.FIVE, Rank.SIX);
        System.out.println(endIndexOfHighestStraight(data4a, RankComparators.ACE_LOW) + " - " + data4a);
        List<Rank> data5 = Arrays.asList(Rank.TWO, Rank.THREE, Rank.FOUR, Rank.FIVE, Rank.ACE);
        System.out.println(endIndexOfHighestStraight(data5) + " - " + data5);
        List<Rank> data6 = Arrays.asList(Rank.TWO, Rank.THREE, Rank.FOUR, Rank.FIVE, Rank.SIX, Rank.ACE);
        System.out.println(endIndexOfHighestStraight(data6) + " - " + data6);
    }
}
